Syntax10.Scn.Fnt StampElems Alloc 22 Apr 96 Syntax10b.Scn.Fnt Syntax10i.Scn.Fnt MarkElems Alloc MODULE LinkElems; (** HM IMPORT Files, Fonts, Display, Input, Viewers, Texts, TextFrames, MenuViewers, TextPrinter, Oberon, PopupElems, MarkElems, FoldElems; CONST left =2; middle = 1; right = 0; pixel = LONG(10000); stdMenu = "System.Close System.Copy System.Grow Edit.Search Edit.Replace Edit.Store "; Elem* = POINTER TO ElemDesc; ElemDesc* = RECORD (Texts.ElemDesc) file*: ARRAY 32 OF CHAR; key*: LONGINT END ; FollowMsg* = RECORD (Texts.ElemMsg) f*: Display.Frame END ; Menu* = POINTER TO MenuDesc; MenuDesc* = RECORD (PopupElems.ElemDesc) END ; Frame = POINTER TO FrameDesc; FrameDesc = RECORD (TextFrames.FrameDesc) e: Elem END ; searchKey: LONGINT; (*key of mark element searched in FollowLink; used in Check*) hint: TextFrames.Frame; (*suggests frame to be used for links emanating from a link menu*) icon, invIcon: Display.Pattern; (* x = 0, y = 3, w = 9, h = 8 *) w: Texts.Writer; PROCEDURE^ New* (file: ARRAY OF CHAR; key: LONGINT): Elem; PROCEDURE GetSelection (VAR f: TextFrames.Frame); (* return frame f with the latest text selection*) VAR v: Viewers.Viewer; f0: Display.Frame; time: LONGINT; x: INTEGER; BEGIN x := 0; time := -1; f := NIL; WHILE x < Display.Width DO v := Viewers.This(x, 0); WHILE v.state > 1 DO f0 := v.dsc; WHILE f0 # NIL DO WITH f0: TextFrames.Frame DO IF f0.hasSel & (f0.time > time) THEN f := f0; time := f.time END ELSE END ; f0 := f0.next END ; v := Viewers.Next(v) END ; x := x + v.W END GetSelection; PROCEDURE GetFileName (t: Texts.Text; VAR name: ARRAY OF CHAR); (* return the name of the viewer which contains text t*) VAR v, V: Viewers.Viewer; f: Display.Frame; s: Texts.Scanner; x: INTEGER; BEGIN V := NIL; x := 0; WHILE x < Display.Width DO v := Viewers.This(x, 0); WHILE v.state > 1 DO f := v.dsc; WHILE f # NIL DO IF (f IS TextFrames.Frame) & (f(TextFrames.Frame).text = t) THEN V := v END ; f := f.next END ; v := Viewers.Next(v) END ; x := x + v.W END ; IF V # NIL THEN Texts.OpenScanner(s, V.dsc(TextFrames.Frame).text, 0); Texts.Scan(s); IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, name) ELSE name[0] := 0X END END GetFileName; PROCEDURE GetFrame (name: ARRAY OF CHAR; VAR f: TextFrames.Frame); VAR x: INTEGER; v: Viewers.Viewer; s: Texts.Scanner; BEGIN IF hint # NIL THEN f := hint ELSE x := 0; f := NIL; WHILE x < Display.Width DO v := Viewers.This(x, 0); WHILE v.state > 1 DO IF (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN Texts.OpenScanner(s, v.dsc(TextFrames.Frame).text, 0); Texts.Scan(s); IF s.s = name THEN f := v.dsc.next(TextFrames.Frame); RETURN END END ; v := Viewers.Next(v) END ; x := x + v.W END END GetFrame; PROCEDURE GetDsr (f: Display.Frame; pos: LONGINT; fnt: Fonts.Font; VAR dsr: INTEGER); VAR p: TextFrames.Parc; beg: LONGINT; BEGIN IF f = NIL THEN IF fnt = NIL THEN dsr := 0 ELSE dsr := - fnt.minY END ELSE TextFrames.ParcBefore(f(TextFrames.Frame).text, pos, p, beg); dsr := SHORT(p.dsr DIV TextFrames.Unit) END GetDsr; PROCEDURE CollectMarks (pop: Menu; f0: Display.Frame); (* Fill popup menu with all mark elements in f0.next.text*) VAR r: Texts.Reader; s: Texts.Scanner; ch: CHAR; f: TextFrames.Frame; mark: MarkElems.Elem; link: Elem; n: INTEGER; file: ARRAY 32 OF CHAR; BEGIN pop.menu := TextFrames.Text(""); IF (f0.next = NIL) OR ~ (f0.next IS TextFrames.Frame) THEN Texts.WriteString(w, "link menu not in menu bar of a text viewer"); Texts.WriteLn(w); Texts.WriteLn(w) ELSE f := f0.next(TextFrames.Frame); hint := f; GetFileName(f.text, file); n := 0; Texts.OpenReader(r, f.text, 0); Texts.ReadElem(r); WHILE ~ r.eot DO IF r.elem IS MarkElems.Elem THEN mark := r.elem(MarkElems.Elem); link := New(file, mark.key); Texts.WriteElem(w, link); Texts.Write(w, " "); Texts.OpenScanner(s, f.text, Texts.Pos(r)); Texts.Scan(s); WHILE ~s.eot & (s.class # Texts.Name) DO Texts.Scan(s) END ; IF ~s.eot THEN Texts.WriteString(w, s.s); Texts.WriteLn(w); INC(n) END END ; Texts.ReadElem(r) END ; IF n = 0 THEN Texts.WriteString(w, "no marks"); Texts.WriteLn(w) END ; IF n <= 1 THEN Texts.WriteLn(w) END ; END ; Texts.Append(pop.menu, w.buf); PopupElems.MeasureMenu(pop) END CollectMarks; PROCEDURE ShowPos (f: TextFrames.Frame; pos: LONGINT); VAR beg, end, delta: LONGINT; BEGIN delta := 200; LOOP beg := f.org; end := TextFrames.Pos(f, f.X + f.W, f.Y); IF (beg <= pos) & (pos < end) OR (delta = 0) THEN EXIT END ; TextFrames.Show(f, pos - delta); delta := delta DIV 2 END ; TextFrames.SetSelection(f, pos, pos + 1) END ShowPos; PROCEDURE Check (e: Texts.Elem): BOOLEAN; BEGIN RETURN (e IS MarkElems.Elem) & (e(MarkElems.Elem).key = searchKey) END Check; PROCEDURE FollowLink* (file: ARRAY OF CHAR; key: LONGINT; backF: Display.Frame; backE: Texts.Elem); VAR v: Viewers.Viewer; menu: TextFrames.Frame; x, y: INTEGER; t: Texts.Text; buf: Texts.Buffer; mark: Texts.Elem; f: TextFrames.Frame; BEGIN GetFrame(file, f); IF f = NIL THEN f := TextFrames.NewText(TextFrames.Text(file), 0); Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); IF Files.Old("Edit.Menu.Text") = NIL THEN menu := TextFrames.NewMenu(file, stdMenu) ELSE menu := TextFrames.NewMenu(file, ""); NEW(t); Texts.Open(t, "Edit.Menu.Text"); NEW(buf); Texts.OpenBuf(buf); Texts.Save(t, 0, t.len, buf); Texts.Append(menu.text, buf) END ; v := MenuViewers.New(menu, f, TextFrames.menuH, x, y) END ; searchKey := key; FoldElems.FindElem(f.text, 0, Check, mark); IF mark # NIL THEN ShowPos(f, Texts.ElemPos(mark)); IF backF # NIL THEN MarkElems.backF := backF(TextFrames.Frame) ELSE MarkElems.backF := NIL END ; MarkElems.backE := backE END FollowLink; PROCEDURE Edit (e: Elem); VAR t: Texts.Text; v: MenuViewers.Viewer; f: Frame; x, y: INTEGER; BEGIN t := TextFrames.Text(""); Texts.WriteString(w, e.file); Texts.Write(w, " "); Texts.WriteInt(w, e.key, 0); Texts.Append(t, w.buf); NEW(f); f.e := e; TextFrames.Open(f, t, 0); Oberon.AllocateSystemViewer(0, x, y); v := MenuViewers.New( TextFrames.NewMenu("LinkElem", "System.Close LinkElems.Update "), f, TextFrames.menuH, x, y) END Edit; PROCEDURE Handle* (e: Texts.Elem; VAR m: Texts.ElemMsg); VAR e1: Elem; x, y, dsr, i: INTEGER; keys: SET; ch: CHAR; follow: FollowMsg; BEGIN WITH e: Elem DO WITH m: Texts.FileMsg DO IF m.id = Texts.load THEN i := 0; REPEAT Files.Read(m.r, ch); e.file[i] := ch; INC(i) UNTIL ch = 0X; Files.ReadLInt(m.r, e.key) ELSE (*Texts.store*) i := 0; REPEAT ch := e.file[i]; Files.Write(m.r, ch); INC(i) UNTIL ch = 0X; Files.WriteLInt(m.r, e.key) END | m: Texts.CopyMsg DO IF m.e = NIL THEN NEW(e1); m.e := e1 ELSE e1 := m.e(Elem) END ; Texts.CopyElem(e, e1); COPY(e.file, e1.file); e1.key := e.key | m: Texts.IdentifyMsg DO m.mod := "LinkElems"; m.proc := "Alloc" | m: TextFrames.DisplayMsg DO IF ~m.prepare THEN GetDsr(m.frame, m.pos, m.fnt, dsr); Display.CopyPattern(Display.white, icon, m.X0, m.Y0+dsr, Display.paint) END | m: TextPrinter.PrintMsg DO IF m.prepare THEN e.W := 1 ELSE e.W := 10 * pixel END | m: TextFrames.TrackMsg DO IF middle IN m.keys THEN IF m.frame # NIL THEN GetDsr(m.frame, m.pos, m.fnt, dsr); Display.CopyPattern(Display.white, icon, m.X0, m.Y0+dsr, Display.invert); Display.CopyPattern(Display.white, invIcon, m.X0, m.Y0+dsr, Display.invert); REPEAT Input.Mouse(keys, x, y); m.keys := m.keys + keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y) UNTIL keys = {}; Display.CopyPattern(Display.white, invIcon, m.X0, m.Y0+dsr, Display.invert); Display.CopyPattern(Display.white, icon, m.X0, m.Y0+dsr, Display.invert) END ; IF m.keys = {middle} THEN follow.f := m.frame; e.handle(e, follow) ELSIF m.keys = {middle, right} THEN Edit(e) END END | m: FollowMsg DO FollowLink(e.file, e.key, m.f, e) ELSE END END Handle; PROCEDURE HandleMenu* (e: Texts.Elem; VAR m: Texts.ElemMsg); VAR e1: Menu; BEGIN WITH e: Menu DO WITH m: Texts.IdentifyMsg DO m.mod := "LinkElems"; m.proc := "AllocMenu" | m: Texts.CopyMsg DO NEW(e1); m.e := e1; PopupElems.Handle(e, m) | m: TextFrames.DisplayMsg DO IF m.prepare THEN e.W := 13 * pixel; e.H := LONG(TextFrames.menuH-1) * pixel; ELSE e.name := ""; PopupElems.Handle(e, m); Display.CopyPattern(Display.white, icon, m.X0+2, m.Y0+3, Display.paint) END | m: TextFrames.TrackMsg DO IF middle IN m.keys THEN CollectMarks(e, m.frame); PopupElems.Handle(e, m); hint := NIL END ELSE PopupElems.Handle(e, m) END END HandleMenu; PROCEDURE Alloc*; VAR e: Elem; BEGIN NEW(e); e.handle := Handle; Texts.new := e END Alloc; PROCEDURE AllocMenu*; VAR e: Menu; BEGIN NEW(e); e.handle := HandleMenu; Texts.new := e END AllocMenu; PROCEDURE New* (file: ARRAY OF CHAR; key: LONGINT): Elem; VAR e: Elem; BEGIN NEW(e); e.W := 10 * pixel; e.H := 11 * pixel; e.handle := Handle; COPY(file, e.file); e.key := key; RETURN e END New; PROCEDURE Insert*; VAR link: Elem; mark: MarkElems.Elem; fromT, toT: Texts.Text; toPos, end, time: LONGINT; r: Texts.Reader; ch: CHAR; m: TextFrames.InsertElemMsg; f: TextFrames.Frame; BEGIN f := Oberon.FocusViewer.dsc(TextFrames.Frame); IF f.hasCar THEN fromT := f.text ELSE fromT := f.next(TextFrames.Frame).text END ; Oberon.GetSelection(toT, toPos, end, time); IF time >= 0 THEN link := New("", 0); GetFileName(toT, link.file); Texts.OpenReader(r, toT, toPos); Texts.Read(r, ch); m.e := link; Viewers.Broadcast(m); IF (ch = Texts.ElemChar) & (r.elem IS MarkElems.Elem) THEN link.key := r.elem(MarkElems.Elem).key ELSE IF (fromT = toT) & (Texts.ElemPos(link) <= toPos) THEN INC(toPos) END ; mark := MarkElems.New(); link.key := mark.key; Texts.WriteElem(w, mark); Texts.Insert(toT, toPos, w.buf) END END Insert; PROCEDURE InsertMenu*; VAR e: Menu; insert: TextFrames.InsertElemMsg; BEGIN NEW(e); e.handle := HandleMenu; e.name := ""; e.menu := TextFrames.Text(""); e.small := TRUE; PopupElems.MeasureMenu(e); insert.e := e; Viewers.Broadcast(insert) END InsertMenu; PROCEDURE Update*; VAR f: Frame; t: Texts.Text; s: Texts.Scanner; r: Texts.Reader; ch: CHAR; BEGIN IF (Oberon.Par.frame = Oberon.Par.vwr.dsc) & (Oberon.Par.frame.next IS Frame) THEN f := Oberon.Par.frame.next(Frame); Texts.OpenScanner(s, f.text, 0); Texts.Scan(s); IF s.class = Texts.Name THEN COPY(s.s, f.e.file); Texts.Scan(s); IF s.class = Texts.Int THEN f.e.key := s.i; t := Oberon.Par.frame(TextFrames.Frame).text; Texts.OpenReader(r, t, t.len-1); Texts.Read(r, ch); IF ch = "!" THEN Texts.Delete(t, t.len-1, t.len) END END END END Update; PROCEDURE InitIcon; VAR line: ARRAY 9 OF SET; BEGIN line[8] := {4}; line[7] := {3, 5}; line[6] := {2, 6}; line[5] := {1..3, 5..7}; line[4] := {3, 5}; line[3] := {3, 5}; line[2] := {3, 5}; line[1] := {3..5}; icon := Display.NewPattern(line, 9, 8); line[8] := {4}; line[7] := {3..5}; line[6] := {2..6}; line[5] := {1..7}; line[4] := {3..5}; line[3] := {3..5}; line[2] := {3..5}; line[1] := {3..5}; invIcon := Display.NewPattern(line, 9, 8) END InitIcon; BEGIN Texts.OpenWriter(w); InitIcon; hint := NIL END LinkElems.